// 对象DOM操作一系列操作进行封装
// 获取元素 操作元素(修改元素的内容(text html value) 修改属性 样式 绑定事件)

// 按功能划分

// 构造函数版 => 如何获取元素?
function GetEle(selector) {
  // 形参 => 用于接收多种参数
  // {}
  // {}[[Prototype]] = GetEle.prototype;
  // this -> {}

  // 将元素统一存放到集合中
  var list = null;
  if (typeof selector == "string") {// ".list li"
    // 接收一个css选择器(字符串)
    list = document.querySelectorAll(selector); // 伪数组 (伪数组直接改[[Prototype]]有问题)
  } else if (selector instanceof Element) {
    //参数是单个HTML元素(元素dom元素)  li  
    list = [selector]; // [li]
  } else if (selector.length >= 0 && typeof selector == "object") {
    // 传入多元素的集合 [li,li,li,li]
    list = selector; // [li,li,li,li]
  }

  list = Array.from(list); // 先转真数组
  Object.setPrototypeOf(list, GetEle.prototype); // 再改伪数组

  return list; //  返回自定义的实例化对象(伪数组 ->元素的集合)  list = [li, li, li, li, li, li]
}

// GetEle.prototype.setText = function (text) {
//     // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
//     // 每次都要先通过实例化对象 -> 获取元素的集合 -> 在操作元素的集合 =>  稍稍有些麻烦
//     for (var i = 0; i < this.length; i++) { //遍历集合中的每个元素
//         var li = this[i];
//         li.textContent = text;
//     }
// }
// GetEle.prototype.getText = function () {
//     return this[0].textContent;  // 默认返回集合中的第一个
// }

// text()   获取和设置集合(实例化对象)中元素的text内容
// text("1111")  有内容就设置
// text()        没有内容就获取

GetEle.prototype.text = function (text) {
  if (arguments.length >= 1) {
    //有至少一个参数
    // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
    // 每次都要先通过实例化对象 -> 获取元素的集合 -> 在操作元素的集合 =>  稍稍有些麻烦
    // for (var i = 0; i < this.length; i++) { //遍历集合中的每个元素
    //     var li = this[i];
    //     li.textContent = text;
    // }

    this.each(function (ele, index) {
      ele.textContent = text;
    });
    return this; // 返回 调用此方法的实例化对象
  } else {
    // 没有参数就获取
    return this[0].textContent; // 默认返回集合中的第一个
  }
};

// html()        获取和设置集合(实例化对象)中元素的html结构
// html("1111")  有内容就设置
// html()        没有内容就获取
GetEle.prototype.html = function (html) {
  if (arguments.length >= 1) {
    //有至少一个参数
    // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
    // 每次都要先通过实例化对象 -> 获取元素的集合 -> 在操作元素的集合 =>  稍稍有些麻烦
    // for (var i = 0; i < this.length; i++) { //遍历集合中的每个元素
    //     var li = this[i];
    //     li.innerHTML = html;
    // }

    this.each(function (ele, index) {
      ele.innerHTML = html;
    });
    return this; // 返回 调用此方法的实例化对象
  } else {
    // 没有参数就获取
    return this[0].innerHTML; // 默认返回集合中的第一个
  }
};

// val()        获取和设置集合(实例化对象)中元素的value结构
// val("1111")  有内容就设置
// val()        没有内容就获取
GetEle.prototype.val = function (con) {
  if (arguments.length >= 1) {
    //有至少一个参数
    // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
    // 每次都要先通过实例化对象 -> 获取元素的集合 -> 在操作元素的集合 =>  稍稍有些麻烦
    // for (var i = 0; i < this.length; i++) { //遍历集合中的每个元素
    //     var li = this[i];
    //     li.value = con;
    // }

    this.each(function (ele, index) {
      ele.value = con;
    });
    return this; // 返回 调用此方法的实例化对象
  } else {
    // 没有参数就获取
    return this[0].value; // 默认返回集合中的第一个
  }
};

// on(type,callbackFn) 绑定事件(事件监听绑定 => 可以给一个元素绑定多个相同类型的事件)
// 可以绑定所有类型的事件
GetEle.prototype.on = function (type, callbackFn) {
  // type:绑定事件类型 callbackFn 事件触发时执行的回调函数
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  // for (var i = 0; i < this.length; i++) {
  //     var ele = this[i];
  //     ele.addEventListener(type, callbackFn)
  // }

  this.each(function (ele, index) {
    ele.addEventListener(type, callbackFn);
  });
  return this; // 返回 调用此方法的实例化对象
};

// 针对特定类型绑定
// click dblclick mousedown -> 基于on方法二次封装得到的
GetEle.prototype.click = function (callbackFn) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.on("click", callbackFn);
  return this; // 返回 调用此方法的实例化对象
};

GetEle.prototype.dblclick = function (callbackFn) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.on("dblclick", callbackFn);
  return this; // 返回 调用此方法的实例化对象
};

GetEle.prototype.mousedown = function (callbackFn) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.on("mousedown", callbackFn);
  return this; // 返回 调用此方法的实例化对象
};

// off(type,callbackFn) 清除绑定事件(事件监听绑定 => 可以给一个元素绑定多个相同类型的事件)
GetEle.prototype.off = function (type, callbackFn) {
  // type:绑定事件类型 callbackFn 事件触发时执行的回调函数
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  // for (var i = 0; i < this.length; i++) {
  //     var ele = this[i];
  //     ele.removeEventListener(type, callbackFn)
  // }

  this.each(function (ele, index) {
    ele.removeEventListener(type, callbackFn);
  });
  return this; // 返回 调用此方法的实例化对象
};

GetEle.prototype.each = function (callbackFn) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]

  // 遍历实例化对象(元素的集合) 每次循环时执行传入的函数
  for (var i = 0; i < this.length; i++) {
    var item = this[i];
    // i
    // this -> 原数组
    // callbackFn(item, i, this);
    callbackFn.call(item, item, i, this); //调用函数 -> 将this指向循环的当前元素
  }
  return this; // 返回 调用此方法的实例化对象
};

// hide()    隐藏集合(实例化对象)中的元素
GetEle.prototype.hide = function () {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.each(function (ele) {
    ele.style.display = "none";
  });
  return this; // 返回 调用此方法的实例化对象
};

// hide()    隐藏集合(实例化对象)中的元素
GetEle.prototype.show = function () {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.each(function (ele) {
    ele.style.display = "block";
  });
  return this; // 返回 调用此方法的实例化对象
};

// addClass  给集合中的每个元素添加一个或多个class名(多个用空格分隔)
// addClass("one two three")
GetEle.prototype.addClass = function (classStr) {
  var arr = classStr.split(" "); // ["one","two","three"]
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  this.each(function (ele) {
    // add() 接收参数的方式
    // ele.classList.add("one", "two", "three");

    ele.classList.add(...arr);
    // ele.classList.add.apply(null, arr);
  });
  return this; // 返回 调用此方法的实例化对象
};

// removeClass  删除集合中的每个元素的一个或多个class名(多个用空格分隔)
// removeClass("one two three")
GetEle.prototype.removeClass = function (classStr) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  var arr = classStr.split(" "); // ["one","two","three"]
  this.each(function (ele) {
    // add() 接收参数的方式
    // ele.classList.remove("one", "two", "three");

    ele.classList.remove(...arr);
    // ele.classList.remove.apply(null, arr);
  });
  return this; // 返回 调用此方法的实例化对象
};

// hasClass()  判断集合中是否存在某个class名 => 只要有一个存在则返回true,都不存在 =>false
GetEle.prototype.hasClass = function (classStr) {
  var flag = false; //假设没有这个class名
  this.each(function (ele) {
    if (ele.classList.contains(classStr)) {
      // 有一个元素存在
      flag = true;
    }
  });
  return flag;
};

// attr()  获取和设置集合中元素的属性节点(存在标签内)  => attribute
// attr("class","one")  有两个参数就设置   ele.setAttribute("class","one")
// attr("class")        一个参数是获取     ele.getAttribute("class")

GetEle.prototype.attr = function (key, val) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  if (arguments.length >= 2) {
    // 有两个参数 => 就设置
    this.each(function (ele) {
      ele.setAttribute(key, val);
    });
  } else if (arguments.length == 1) {
    //一个参数就获取
    return this[0].getAttribute(key);
  }
  return this; // 返回 调用此方法的实例化对象
};

// prop()   获取和设置集合中元素节点的属性(元素内置属性) => ele.xxx
// prop("className","one")   有两个参数就设置   ele.className="one"
// prop("className")         一个参数是获取     ele.className

GetEle.prototype.prop = function (key, val) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  if (arguments.length >= 2) {
    // 有两个参数 => 就设置
    this.each(function (ele) {
      ele[key] = val;
    });
  } else if (arguments.length == 1) {
    //一个参数就获取
    return this[0][key];
  }

  return this; // 返回 调用此方法的实例化对象
};

// css()  获取和设置集合中每个元素css样式
// css("width","200px")  两个参数就设置  ele.style.width = "200px";
// css("width")          一个参数就获取  ele.style.width(只能获取内联) -> getComputedStyle
// css({width:"200px",height:"200px"}) => 传入一个对象批量设置

GetEle.prototype.css = function (cssKey, cssVal) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  if (arguments.length >= 2) {
    // 有两个参数 => 就设置
    this.each(function (ele) {
      ele.style[cssKey] = cssVal;
    });
    return this; // 返回 调用此方法的实例化对象
  } else if (arguments.length == 1) {
    //一个参数就获取
    if (typeof cssKey == "string") {
      // css("width")   cssKey =>  "width"
      var ele = this[0];
      return window.getComputedStyle(ele)[cssKey];
    } else if (typeof cssKey == "object") {
      // css({width:"200px",height:"200px"})   => cssKey = {width:"200px",height:"200px"}
      for (var key in cssKey) {
        var val = cssKey[key];
        this.css(key, val);
      }
      return this; // 返回 调用此方法的实例化对象
    }
  }
};

// width()   获取和设置集合中元素的宽度
// width(200) / width("200px")   有参数就设置
// width()             没有参数就获取
GetEle.prototype.width = function (val) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  if (arguments.length >= 1) {
    //有参数

    // 判断有没有单位
    // 有单位 px pt % em rem vw vh vmin vmax  => 直接拼接
    // 没有单位  => 默认px

    // 赋值时记得加单位
    var reg = /^\d+(\.\d+)?(px|pt|em|rem|vw|vh|vmin|vmax)$/;

    if (reg.test(val)) {
      //有单位
      this.css("width", val);
    } else {
      // 没有
      this.css("width", val + "px");
    }

    return this; // 返回 调用此方法的实例化对象
  } else {
    // 没有参数就获取
    // 取值时记得去单位
    return parseFloat(this.css("width"));
  }
};

GetEle.prototype.height = function (val) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  if (arguments.length >= 1) {
    //有参数

    // 判断有没有单位
    // 有单位 px pt % em rem vw vh vmin vmax  => 直接拼接
    // 没有单位  => 默认px

    // 赋值时记得加单位
    var reg = /^\d+(\.\d+)?(px|pt|em|rem|vw|vh|vmin|vmax)$/;

    if (reg.test(val)) {
      //有单位
      this.css("height", val);
    } else {
      // 没有
      this.css("height", val + "px");
    }
    return this; // 返回 调用此方法的实例化对象
  } else {
    // 没有参数就获取
    // 取值时记得去单位
    return parseFloat(this.css("height"));
  }
};

// eq()  从集合中匹配对应下标的元素(形成的实例化对象)
// $(".list li").eq(0).css({ background: "red" });

GetEle.prototype.eq = function (index) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]
  var ele = this[index]; // 获取对应下标的元素(原生dom元素)
  return $(ele); //   从集合中匹配对应下标的元素(形成的实例化对象)
};

//$(".list li") => [li,li,li,li,li,li]
//$(".list li").eq(3) => [li]

// siblings()  除了被选中元素以外 所有的同级元素
// $(".list li").eq(0).siblings().css({ background: "red" });
GetEle.prototype.siblings = function (index) {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]

  var ele = this[0]; //先获取被选中的元素()
  var parentEle = ele.parentElement; // 找到父元素
  var children = parentEle.children; // 所有的子元素(包含自己)

  var list = Array.from(children).filter((item) => item !== ele); // 找到除自己以外的所有同级元素

  return $(list);
};

// index()   返回匹配的元素相对于同级元素的下标

GetEle.prototype.index = function () {
  // this -> 调用此方法的实例化对象(伪数组 -> 元素的集合)  [li,li,li,li,li,li]

  var ele = this[0]; //先获取匹配的元素()
  var parentEle = ele.parentElement; // 找到父元素
  var children = parentEle.children; // 所有的子元素(包含自己)

  var index = Array.from(children).find((item) => item === ele); // 找到除自己以外的所有同级元素
  return index;
};

// 封装一个方法 根据传入的选择器 快速得到实例化对象
function $(selector) {
  return new GetEle(selector);
}

$.ajax = function (options) { // 对象  {url,data,async,dataType,success}

        var { type = "get", url, data = "", async = true, dataType = "text", success } = options;

        var xhr = new XMLHttpRequest();

        if (Object.prototype.toString.call(data) == "[object Object]") {
            var list = [];
            for (var key in data) {
                var val = data[key];
                list.push(`${key}=${val}`);
            }
            data = list.join("&");
        }
        // console.log(data);

        if (type.toLowerCase() == "get") {
            xhr.open("get", data ? (url + "?" + data) : url, async);
            xhr.send();
        } else if (type.toLowerCase() == "post") {
            xhr.open("post", url, async);
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(data);
        }

        xhr.onreadystatechange = function () {

            if (xhr.readyState == 4 && xhr.status == 200) {
                var result = xhr.responseText; //获取响应的文本
                if (dataType == "json") {
                    result = JSON.parse(result);
                }
                // console.log(result);

                if (success && typeof success == "function") {
                    success.call(xhr, result); //请求成功时执行 传入响应数据
                }
            }
        }
}
 
export { $ }